home *** CD-ROM | disk | FTP | other *** search
/ QRZ! Ham Radio 6 / QRZ Ham Radio Callsign Database - Volume 6.iso / mac / files / amiga / rhinosrc.lha / popserv.c < prev    next >
C/C++ Source or Header  |  1993-06-09  |  14KB  |  725 lines

  1. /* POP Server state machine - see RFC 937
  2.  *
  3.  *  also see other credits in popcli.c
  4.  *  10/89 Mike Stockett wa7dyx
  5.  *  Modified 5/27/90 by Allen Gwinn, N5CKP, for later NOS releases.
  6.  *  Added to NOS by PA0GRI 2/6/90 (and linted into "standard" C)
  7.  */
  8.  
  9. #include <stdio.h>
  10. #include <time.h>
  11. #include <sys/stat.h>
  12. #ifdef UNIX
  13. #include <sys/types.h>
  14. #endif
  15. #if    defined(__STDC__) || defined(__TURBOC__)
  16. #include <stdarg.h>
  17. #endif
  18. #include <ctype.h>
  19. #include <setjmp.h>
  20. #include "global.h"
  21. #include "mbuf.h"
  22. #include "cmdparse.h"
  23. #include "socket.h"
  24. #include "proc.h"
  25. #include "files.h"
  26. #include "pop.h"
  27. #include "commands.h"
  28.  
  29. extern char Nospace[];
  30.  
  31. static struct pop_scb *create_scb __ARGS((void));
  32. static void delete_scb __ARGS((struct pop_scb *scb));
  33. static void popserv __ARGS((int s,void *unused,void *p));
  34. static int poplogin __ARGS((char *pass,char *username));
  35. void rrip(char *s);
  36.  
  37. /* I don't know why this isn't static, it isn't called anywhere else {was} */
  38. void pop_sm __ARGS((struct pop_scb *scb));
  39.  
  40. static int Spop = -1; /* prototype socket for service */
  41.  
  42. /* Start up POP receiver service */
  43. int
  44. pop1(argc,argv,p)
  45.  
  46. int argc;
  47. char *argv[];
  48. void *p;
  49.  
  50. {
  51.     struct sockaddr_in lsocket;
  52.     int s;
  53.  
  54.     if (Spop != -1) {
  55.         return 0;
  56.     }
  57.  
  58.     psignal(Curproc,0);        /* Don't keep the parser waiting */
  59.     chname(Curproc,"POP listener");
  60.  
  61.     lsocket.sin_family = AF_INET;
  62.     lsocket.sin_addr.s_addr = INADDR_ANY;
  63.     if(argc < 2)
  64.         lsocket.sin_port = IPPORT_POP;
  65.     else
  66.         lsocket.sin_port = atoi(argv[1]);
  67.  
  68.     Spop = socket(AF_INET,SOCK_STREAM,0);
  69.  
  70.     bind(Spop,(char *)&lsocket,sizeof(lsocket));
  71.  
  72.     listen(Spop,1);
  73.  
  74.     for (;;) {
  75.         if((s = accept(Spop,NULLCHAR,(int *)NULL)) == -1)
  76.             break;    /* Service is shutting down */
  77.  
  78.         /* Spawn a server */
  79.  
  80.         newproc("POP server",2048,popserv,s,NULL,NULL,0);
  81.     }
  82.     return 0;
  83. }
  84.  
  85. /* Shutdown POP service (existing connections are allowed to finish) */
  86.  
  87. int
  88. pop0(argc,argv,p)
  89. int argc;
  90. char *argv[];
  91. void *p;
  92.  
  93. {
  94.     close_s(Spop);
  95.     Spop = -1;
  96.     return 0;
  97. }
  98.  
  99. static void
  100. popserv(s,unused,p)
  101. int s;
  102. void *unused;
  103. void *p;
  104. {
  105.     struct pop_scb *scb;
  106.     FILE *network;
  107.  
  108.     sockowner(s,Curproc);        /* We own it now */
  109.     log(s,"open POP");
  110.     network = fdopen(s,"r+t");
  111.  
  112.     if((scb = create_scb()) == NULLSCB) {
  113.         printf(Nospace);
  114.         log(s,"close POP - no space");
  115.         fclose(network);
  116.         return;
  117.     }
  118.  
  119.     scb->network = network;
  120.     scb->state  = AUTH;
  121.  
  122.     (void) fprintf(network,greeting_msg,Hostname);
  123.  
  124. loop:    if (fgets(scb->buf,BUF_LEN,network) == NULLCHAR){
  125.         /* He closed on us */
  126.  
  127.         goto quit;
  128.     }
  129.     scb->count = strlen(scb->buf);
  130.     rip(scb->buf);
  131.     if (strlen(scb->buf) == 0)        /* Ignore blank cmd lines */
  132.         goto loop;
  133.     pop_sm(scb);
  134.     if (scb->state == DONE)
  135.         goto quit;
  136.  
  137.     goto loop;
  138.  
  139. quit:
  140.     log(fileno(scb->network),"close POP");
  141.     fclose(scb->network);
  142.     delete_scb(scb);
  143. }
  144.  
  145.  
  146. /* Create control block, initialize */
  147.  
  148. static struct
  149. pop_scb *create_scb()
  150. {
  151.     register struct pop_scb *scb;
  152.  
  153.     if((scb = (struct pop_scb *)callocw(1,sizeof (struct pop_scb))) == NULLSCB)
  154.         return NULLSCB;
  155.  
  156.     scb->username[0] = '\0';
  157.     scb->msg_status = NULL;
  158.     scb->wf = NULL;
  159.  
  160.     scb->count = scb->folder_file_size = scb->msg_num = 0;
  161.  
  162.     scb->folder_modified = FALSE;
  163.     return scb;
  164. }
  165.  
  166.  
  167. /* Free resources, delete control block */
  168.  
  169. static void
  170. delete_scb(scb)
  171. register struct pop_scb *scb;
  172. {
  173.  
  174.     if (scb == NULLSCB)
  175.         return;
  176.     if (scb->wf != NULL)
  177.         fclose(scb->wf);
  178.     if (scb->msg_status  != NULL)
  179.         free((char *)scb->msg_status);
  180.  
  181.     free((char *)scb);
  182. }
  183.  
  184. /* replace terminating end of line marker(s) (\r and \n) with null */
  185. void
  186. rrip(s)
  187. register char *s;
  188. {
  189.     register char *cp;
  190.  
  191.     if((cp = strchr(s,'\r')) != NULLCHAR)
  192.         *cp = '\0';
  193.     if((cp = strchr(s,'\n')) != NULLCHAR)
  194.         *cp = '\0';
  195. }
  196.  
  197. /* --------------------- start of POP server code ------------------------ */
  198.  
  199. #define    BITS_PER_WORD        16
  200.  
  201. #define isSOM(x)        ((strncmp(x,"From ",5) == 0))
  202.  
  203. /* Command string specifications */
  204.  
  205. static char    ackd_cmd[] = "ACKD",
  206.         acks_cmd[] = "ACKS",
  207. #ifdef POP_FOLDERS
  208.         fold_cmd[] = "FOLD ",
  209. #endif
  210.         login_cmd[] = "HELO ",
  211.         nack_cmd[] = "NACK",
  212.         quit_cmd[] = "QUIT",
  213.         read_cmd[] = "READ",
  214.         retr_cmd[] = "RETR";
  215.  
  216. void
  217. pop_sm(scb)
  218. struct pop_scb *scb;
  219. {
  220.     char password[40];
  221.     void state_error(struct pop_scb *,char *);
  222.     void open_folder(struct pop_scb *);
  223.     void do_cleanup(struct pop_scb *);
  224.     void read_message(struct pop_scb *);
  225.     void retrieve_message(struct pop_scb *);
  226.     void deletemsg(struct pop_scb *,int);
  227.     void get_message(struct pop_scb *,int);
  228.     void print_message_length(struct pop_scb *);
  229.     void close_folder(struct pop_scb *);
  230. #ifdef POP_FOLDERS
  231.     void select_folder(struct pop_scb *);
  232. #endif
  233.  
  234.     if (scb == NULLSCB)    /* be certain it is good -- wa6smn */
  235.         return;
  236.  
  237.     switch(scb->state) {
  238.     case AUTH:
  239.         if (strncmp(scb->buf,login_cmd,strlen(login_cmd)) == 0){
  240.             sscanf(scb->buf,"HELO %s%s",scb->username,password);
  241.  
  242.             if (!poplogin(scb->username,password)) {
  243.                 log(fileno(scb->network),"POP access DENIED to %s",
  244.                         scb->username);
  245.                 state_error(scb,"Access DENIED!!");
  246.                 return;
  247.             }
  248.  
  249.             log(fileno(scb->network),"POP access granted to %s",
  250.                     scb->username);
  251.             open_folder(scb);
  252.         } else if (strncmp(scb->buf,quit_cmd,strlen(quit_cmd)) == 0){
  253.             do_cleanup(scb);
  254.         } else
  255.             state_error(scb,"(AUTH) Expected HELO or QUIT command");
  256.         break;
  257.  
  258.     case MBOX:
  259.         if (strncmp(scb->buf,read_cmd,strlen(read_cmd)) == 0)
  260.             read_message(scb);
  261.  
  262. #ifdef POP_FOLDERS
  263.         else if (strncmp(scb->buf,fold_cmd,strlen(fold_cmd)) == 0)
  264.             select_folder(scb);
  265.  
  266. #endif
  267.  
  268.         else if (strncmp(scb->buf,quit_cmd,strlen(quit_cmd)) == 0) {
  269.             do_cleanup(scb);
  270.         } else
  271.             state_error(scb,
  272. #ifdef POP_FOLDERS
  273.                     "(MBOX) Expected FOLD, READ, or QUIT command");
  274. #else
  275.                     "(MBOX) Expected READ or QUIT command");
  276. #endif
  277.         break;
  278.  
  279.     case ITEM:
  280.         if (strncmp(scb->buf,read_cmd,strlen(read_cmd)) == 0)
  281.             read_message(scb);
  282.  
  283. #ifdef POP_FOLDERS
  284.  
  285.         else if (strncmp(scb->buf,fold_cmd,strlen(fold_cmd)) == 0)
  286.             select_folder(scb);
  287. #endif
  288.  
  289.         else if (strncmp(scb->buf,retr_cmd,strlen(retr_cmd)) == 0)
  290.             retrieve_message(scb);
  291.         else if (strncmp(scb->buf,quit_cmd,strlen(quit_cmd)) == 0)
  292.             do_cleanup(scb);
  293.         else
  294.             state_error(scb,
  295. #ifdef POP_FOLDERS
  296.                "(ITEM) Expected FOLD, READ, RETR, or QUIT command");
  297. #else
  298.                "(ITEM) Expected READ, RETR, or QUIT command");
  299. #endif
  300.         break;
  301.  
  302.     case NEXT:
  303.         if (strncmp(scb->buf,ackd_cmd,strlen(ackd_cmd)) == 0){
  304.                 /* ACKD processing */
  305.             deletemsg(scb,scb->msg_num);
  306.             scb->msg_num++;
  307.             get_message(scb,scb->msg_num);
  308.         } else if (strncmp(scb->buf,acks_cmd,strlen(acks_cmd)) == 0){
  309.                 /* ACKS processing */
  310.             scb->msg_num++;
  311.             get_message(scb,scb->msg_num);
  312.         } else if (strncmp(scb->buf,nack_cmd,strlen(nack_cmd)) == 0){
  313.                 /* NACK processing */
  314.             fseek(scb->wf,scb->curpos,SEEK_SET);
  315.         } else {
  316.             state_error(scb,"(NEXT) Expected ACKD, ACKS, or NACK command");
  317.             return;
  318.         }
  319.  
  320.         print_message_length(scb);
  321.         scb->state  = ITEM;
  322.         break;
  323.  
  324.     case DONE:
  325.         do_cleanup(scb);
  326.         break;
  327.  
  328.     default:
  329.         state_error(scb,"(TOP) State Error!!");
  330.         break;
  331.     }
  332. }
  333.  
  334. void
  335. do_cleanup(scb)
  336. struct pop_scb *scb;
  337. {
  338.     void close_folder(struct pop_scb *);
  339.  
  340.     close_folder(scb);
  341.     (void) fprintf(scb->network,signoff_msg);
  342.     scb->state = DONE;
  343. }
  344.  
  345. void
  346. state_error(scb,msg)
  347. struct pop_scb *scb;
  348. char *msg;
  349. {
  350.     (void) fprintf(scb->network,error_rsp,msg);
  351.     scb->state = DONE;
  352. }
  353.  
  354. #ifdef POP_FOLDERS
  355.  
  356. select_folder(scb)
  357. struct pop_scb    *scb;
  358. {
  359.     sscanf(scb->buf,"FOLD %s",scb->username);
  360.  
  361.     if (scb->wf != NULL)
  362.         close_folder(scb);
  363.  
  364.     open_folder(scb);
  365. }
  366.  
  367. #endif
  368.  
  369.  
  370. void
  371. close_folder(scb)
  372. struct pop_scb *scb;
  373. {
  374.     char folder_pathname[64];
  375.     char line[BUF_LEN];
  376.     FILE *fd;
  377.     int deleted = FALSE;
  378.     int msg_no = 0;
  379.     struct stat folder_stat;
  380.     int newmail(struct pop_scb *);
  381.     int isdeleted(struct pop_scb *,int);
  382.  
  383.     if (scb->wf == NULL)
  384.         return;
  385.  
  386.     if (!scb->folder_modified) {
  387.         /* no need to re-write the folder if we have not modified it */
  388.  
  389.         fclose(scb->wf);
  390.         scb->wf = NULL;
  391.  
  392.         free((char *)scb->msg_status);
  393.         scb->msg_status = NULL;
  394.         return;
  395.     }
  396.  
  397.  
  398.     sprintf(folder_pathname,"%s/%s.txt",Mailspool,scb->username);
  399.  
  400.     if (newmail(scb)) {
  401.         /* copy new mail into the work file and save the
  402.            message count for later */
  403.  
  404.         if ((fd = fopen(folder_pathname,"r")) == NULL) {
  405.             state_error(scb,"Unable to add new mail to folder");
  406.             return;
  407.         }
  408.  
  409.         fseek(scb->wf,0,SEEK_END);
  410.         fseek(fd,scb->folder_file_size,SEEK_SET);
  411.         while (!feof(fd)) {
  412.             fgets(line,BUF_LEN,fd);
  413.             fputs(line,scb->wf);
  414.         }
  415.  
  416.         fclose(fd);
  417.     }
  418.  
  419.     /* now create the updated mail folder */
  420.  
  421.     if ((fd = fopen(folder_pathname,"w")) == NULL){
  422.         state_error(scb,"Unable to update mail folder");
  423.         return;
  424.     }
  425.  
  426.     rewind(scb->wf);
  427.     while (!feof(scb->wf)){
  428.         fgets(line,BUF_LEN,scb->wf);
  429.  
  430.         if (isSOM(line)){
  431.             msg_no++;
  432.             if (msg_no <= scb->folder_len)
  433.                 deleted = isdeleted(scb,msg_no);
  434.             else
  435.                 deleted = FALSE;
  436.         }
  437.  
  438.         if (deleted)
  439.             continue;
  440.  
  441.         fputs(line,fd);
  442.     }
  443.  
  444.     fclose(fd);
  445.  
  446.     /* trash the updated mail folder if it is empty */
  447.  
  448.     if ((stat(folder_pathname,&folder_stat) == 0) && (folder_stat.st_size == 0))
  449.         unlink(folder_pathname);
  450.  
  451.     fclose(scb->wf);
  452.     scb->wf = NULL;
  453.  
  454.     free((char *)scb->msg_status);
  455.     scb->msg_status = NULL;
  456. }
  457.  
  458. void
  459. open_folder(scb)
  460. struct pop_scb    *scb;
  461. {
  462.     char folder_pathname[64];
  463.     char line[BUF_LEN];
  464.     FILE *fd;
  465.     FILE *tmpfile();
  466.     struct stat folder_stat;
  467.  
  468.  
  469.     sprintf(folder_pathname,"%s/%s.txt",Mailspool,scb->username);
  470.     scb->folder_len       = 0;
  471.     scb->folder_file_size = 0;
  472.     if (stat(folder_pathname,&folder_stat)){
  473.          (void) fprintf(scb->network,no_mail_rsp);
  474.          return;
  475.     }
  476.  
  477.     scb->folder_file_size = folder_stat.st_size;
  478.     if ((fd = fopen(folder_pathname,"r")) == NULL){
  479.         state_error(scb,"Unable to open mail folder");
  480.         return;
  481.     }
  482.  
  483.     if ((scb->wf = tmpfile()) == NULL) {
  484.         state_error(scb,"Unable to create work folder");
  485.         return;
  486.     }
  487.  
  488.     while(!feof(fd)) {
  489.         fgets(line,BUF_LEN,fd);
  490.  
  491.         /* scan for begining of a message */
  492.  
  493.         if (isSOM(line))
  494.             scb->folder_len++;
  495.  
  496.         /* now put  the line in the work file */
  497.  
  498.         fputs(line,scb->wf);
  499.     }
  500.  
  501.     fclose(fd);
  502.  
  503.     scb->msg_status_size = (scb->folder_len) / BITS_PER_WORD;
  504.  
  505.     if ((((scb->folder_len) % BITS_PER_WORD) != 0) ||
  506.         (scb->msg_status_size == 0))
  507.         scb->msg_status_size++;
  508.  
  509.     if ((scb->msg_status = (unsigned int *) callocw(scb->msg_status_size,
  510.                 sizeof(unsigned int))) == NULL) {
  511.         state_error(scb,"Unable to create message status array");
  512.         return;
  513.     }
  514.  
  515.     (void) fprintf(scb->network,count_rsp,scb->folder_len);
  516.  
  517.     scb->state  = MBOX;
  518. }
  519.  
  520. void
  521. read_message(scb)
  522. struct pop_scb    *scb;
  523. {
  524.     void get_message(struct pop_scb *,int);
  525.     void print_message_length(struct pop_scb *);
  526.  
  527.     if (scb == NULLSCB)    /* check for null -- wa6smn */
  528.         return;
  529.     if (scb->buf[sizeof(read_cmd) - 1] == ' ')
  530.         scb->msg_num = atoi(&(scb->buf[sizeof(read_cmd) - 1]));
  531.     else
  532.         scb->msg_num++;
  533.  
  534.     get_message(scb,scb->msg_num);
  535.     print_message_length(scb);
  536.     scb->state  = ITEM;
  537. }
  538.  
  539. void
  540. retrieve_message(scb)
  541. struct pop_scb    *scb;
  542. {
  543.     char line[BUF_LEN];
  544.     long cnt;
  545.  
  546.     if (scb == NULLSCB)    /* check for null -- wa6smn */
  547.         return;
  548.     if (scb->msg_len == 0) {
  549.         state_error(scb,"Attempt to access a DELETED message!");
  550.         return;
  551.     }
  552.  
  553.     cnt  = scb->msg_len;
  554.     while(!feof(scb->wf) && (cnt > 0)) {
  555.         fgets(line,BUF_LEN,scb->wf);
  556.         rrip(line);
  557.  
  558.         (void) fprintf(scb->network,msg_line,line);
  559.         cnt -= (strlen(line)+2);    /* Compensate for CRLF */
  560.     }
  561.  
  562.     scb->state = NEXT;
  563. }
  564.  
  565. void
  566. get_message(scb,msg_no)
  567. struct pop_scb    *scb;
  568. int msg_no;
  569. {
  570.     char line[BUF_LEN];
  571.     long ftell();
  572.  
  573.     if (scb == NULLSCB)    /* check for null -- wa6smn */
  574.         return;
  575.     scb->msg_len = 0;
  576.     if (msg_no > scb->folder_len) {
  577.         scb->curpos  = 0;
  578.         scb->nextpos = 0;
  579.         return;
  580.     } else {
  581.         /* find the message and its length */
  582.  
  583.         rewind(scb->wf);
  584.         while (!feof(scb->wf) && (msg_no > -1)) {
  585.             if (msg_no > 0)
  586.                 scb->curpos = ftell(scb->wf);
  587.             
  588.             fgets(line,BUF_LEN,scb->wf);
  589.             rrip(line);
  590.  
  591.             if (isSOM(line))
  592.                 msg_no--;
  593.  
  594.             if (msg_no != 0)
  595.                 continue;
  596.  
  597.             scb->nextpos  = ftell(scb->wf);
  598.             scb->msg_len += (strlen(line)+2);    /* Add CRLF */
  599.         }
  600.     }
  601.  
  602.     if (scb->msg_len > 0)
  603.         fseek(scb->wf,scb->curpos,SEEK_SET);
  604.  
  605.     /* we need the pointers even if the message was deleted */
  606.  
  607.     if  (isdeleted(scb,scb->msg_num))
  608.         scb->msg_len = 0;
  609. }
  610.  
  611. static int
  612. poplogin(username,pass)
  613. char *pass;
  614. char *username;
  615. {
  616.     char buf[80];
  617.     char *cp;
  618.     char *cp1;
  619.     FILE *fp;
  620.  
  621.     if((fp = fopen(Popusers,"r")) == NULLFILE) {
  622.         /* User file doesn't exist */
  623.         printf("POP users file %s not found\n",Popusers);
  624.         return(FALSE);
  625.     }
  626.  
  627.     while(fgets(buf,sizeof(buf),fp),!feof(fp)) {
  628.         if(buf[0] == '#')
  629.             continue;    /* Comment */
  630.  
  631.         if((cp = strchr(buf,':')) == NULLCHAR)
  632.             /* Bogus entry */
  633.             continue;
  634.  
  635.         *cp++ = '\0';        /* Now points to password */
  636.         if(strcmp(username,buf) == 0)
  637.             break;        /* Found user name */
  638.     }
  639.  
  640.     if(feof(fp)) {
  641.         /* User name not found in file */
  642.  
  643.         fclose(fp);
  644.         return(FALSE);
  645.     }
  646.     fclose(fp);
  647.  
  648.     if ((cp1 = strchr(cp,':')) == NULLCHAR)
  649.         return(FALSE);
  650.  
  651.     *cp1 = '\0';
  652.     if(strcmp(cp,pass) != 0) {
  653.         /* Password required, but wrong one given */
  654.  
  655.         return(FALSE);
  656.     }
  657.  
  658.     /* whew! finally made it!! */
  659.  
  660.     return(TRUE);
  661. }
  662.  
  663. int
  664. isdeleted(scb,msg_no)
  665. struct pop_scb *scb;
  666. int msg_no;
  667. {
  668.     unsigned int mask = 1,offset;
  669.  
  670.     msg_no--;
  671.     offset = msg_no / BITS_PER_WORD;
  672.     mask <<= msg_no % BITS_PER_WORD;
  673.     return (((scb->msg_status[offset]) & mask)? TRUE:FALSE);
  674. }
  675.  
  676. void
  677. deletemsg(scb,msg_no)
  678. struct pop_scb *scb;
  679. int msg_no;
  680. {
  681.     unsigned int mask = 1,offset;
  682.  
  683.     if (scb == NULLSCB)    /* check for null -- wa6smn */
  684.         return;
  685.     msg_no--;
  686.     offset = msg_no / BITS_PER_WORD;
  687.     mask <<= msg_no % BITS_PER_WORD;
  688.     scb->msg_status[offset] |= mask;
  689.     scb->folder_modified = TRUE;
  690. }
  691.  
  692. int
  693. newmail(scb)
  694. struct pop_scb *scb;
  695. {
  696.     char folder_pathname[64];
  697.     struct stat folder_stat;
  698.  
  699.     sprintf(folder_pathname,"%s/%s.txt",Mailspool,scb->username);
  700.  
  701.     if (stat(folder_pathname,&folder_stat)) {
  702.         state_error(scb,"Unable to get old mail folder's status");
  703.         return(FALSE);
  704.     } else
  705.         return ((folder_stat.st_size > scb->folder_file_size)? TRUE:FALSE);
  706. }
  707.  
  708. void
  709. print_message_length(scb)
  710. struct pop_scb *scb;
  711. {
  712.     char *print_control_string;
  713.  
  714.     if (scb == NULLSCB)    /* check for null -- wa6smn */
  715.         return;
  716.     if (scb->msg_len > 0)
  717.         print_control_string = length_rsp;
  718.     else if (scb->msg_num <= scb->folder_len)
  719.         print_control_string = length_rsp;
  720.     else
  721.         print_control_string = no_more_rsp;
  722.  
  723.     (void)fprintf(scb->network,print_control_string,scb->msg_len,scb->msg_num);
  724. }
  725.